/*
   Copyright 2007 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "cpputils/TestUtils.hpp"
#include "cpputils/io/StreamReframer.hpp"

BOOST_AUTO_TEST_CASE ( TestByteStream ) {
	ByteArrayInputOutputStream::SharedPtr stream ( new ByteArrayInputOutputStream(5));
	BOOST_REQUIRE_MESSAGE ( stream.get(), "Instance is null" );
	ByteBuffer::SharedPtr buffer1 (new ByteBuffer ( 10 ) );
	buffer1->setLen ( 10 );

	ByteBuffer::SharedPtr buffer2 (new ByteBuffer (  5 ) );
	buffer2->setLen ( 5 );

	ByteBuffer::SharedPtr buffer3 (new ByteBuffer ( 4 ) );
	buffer3->setLen ( 4 );

	stream->write ( buffer1.get() );
	stream->write ( buffer2.get() );
	stream->write ( buffer3.get() );

	buffer1->setLen (5);
	stream->read ( buffer1.get() );
	buffer1->setLen (0);
	stream->read ( buffer1.get() );
}

BOOST_AUTO_TEST_CASE ( TestByteStream2 ) {
	ByteArrayInputOutputStream::SharedPtr stream ( new ByteArrayInputOutputStream(4));
	stream->setDumpMode(false);
	BOOST_REQUIRE_MESSAGE ( stream.get(), "Instance is null" );
	ByteBuffer::SharedPtr buffer1 (new ByteBuffer ( 6 ) );
	ByteBuffer::SharedPtr buffer2 (new ByteBuffer ( 6 ) );
	buffer1->getData()[0] = 0x0A;
	buffer1->getData()[1] = 0x0B;
	buffer1->getData()[2] = 0x0C;
	buffer1->getData()[3] = 0x0D;
	buffer1->getData()[4] = 0x0E;
	buffer1->getData()[5] = 0x0F;
	buffer1->setLen(6);
	for(unsigned int i=0;i<4;i++) {
		stream->write(buffer1.get());
		buffer2->fill(0);
		buffer2->setLen(0);
		stream->read(buffer2.get());
		checkBuffers(buffer1.get(), buffer2.get());
	}
}

BOOST_AUTO_TEST_CASE ( TestStreamReframer ) {
	ByteArrayInputOutputStream stream;
	StreamReframer reframer;
	reframer.setDestination(&stream);
	reframer.setFrameSize(4);
	ByteBuffer::SharedPtr buffer1 (new ByteBuffer ( 6 ) );
	buffer1->getData()[0] = 0x0A;
	buffer1->getData()[1] = 0x0B;
	buffer1->getData()[2] = 0x0C;
	buffer1->getData()[3] = 0x0D;
	buffer1->getData()[4] = 0x0E;
	buffer1->getData()[5] = 0x0F;
	buffer1->setLen(6);

	ByteBuffer::SharedPtr buffer2 (new ByteBuffer ( 4 ) );
	reframer.write(buffer1.get());

	// Reading the first part
	stream.read(buffer2.get());
	buffer1->setLen(4);
	checkBuffers(buffer1.get(), buffer2.get());

	// Reading the last part
	buffer2->setLen(0);
	buffer2->fill(0);
	stream.read(buffer2.get());

	// Checking the last part
	BOOST_REQUIRE_MESSAGE ( buffer2->getLen() == 2, "Buffer2 has invalid len");
	BOOST_REQUIRE_MESSAGE ( buffer2->getData()[1] == 0x0F, "Buffer2 has invalid content");
}

BOOST_AUTO_TEST_CASE ( TestBufferCoping ) {
	// Create buffer
	const unsigned char aBuffer[] =  { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
	ByteBuffer oBuffer( sizeof(aBuffer) );
	oBuffer.append(aBuffer, sizeof(aBuffer));

	// Create a new copy buffer based in current
	ByteBuffer oNewBuffer ( oBuffer );
	checkBuffers ( &oNewBuffer, &oBuffer );
}

BOOST_AUTO_TEST_CASE ( TestBufferCut ) {
	// Create buffer
	const unsigned char aBuffer[] =  { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
	ByteBuffer oBuffer( sizeof(aBuffer) );
	oBuffer.append(aBuffer, sizeof(aBuffer));

	ByteBuffer oChkBuffer( sizeof(aBuffer) );

	const unsigned char aChkBuffer1[] =  { 0x0D, 0x0E, 0x0F };
	oChkBuffer.replace( aChkBuffer1, sizeof(aChkBuffer1) );
	oBuffer.cut(0, 3); // Cut first 3 bytes
	checkBuffers ( &oChkBuffer, &oBuffer );

	oBuffer.replace ( aBuffer, sizeof(aBuffer) );
	const unsigned char aChkBuffer2[] =  { 0x0A, 0x0B, 0x0C };
	oChkBuffer.replace( aChkBuffer2, sizeof(aChkBuffer2) );
	oBuffer.cut(3); // Skip first 3 bytes and remove all others
	checkBuffers ( &oChkBuffer, &oBuffer );

	oBuffer.replace ( aBuffer, sizeof(aBuffer) );
	const unsigned char aChkBuffer3[] =  { 0x0A, 0x0B, 0x0C, 0x0F };
	oChkBuffer.replace( aChkBuffer3, sizeof(aChkBuffer3) );
	oBuffer.cut(3,5); // Cut from 3 to 5
	checkBuffers ( &oChkBuffer, &oBuffer );

	ByteBuffer oNewBuffer;
	oBuffer.replace ( aBuffer, sizeof(aBuffer) );
	const unsigned char aChkBuffer4[] =  { 0x0A, 0x0D, 0x0E, 0x0F };
	oChkBuffer.replace( aChkBuffer4, sizeof(aChkBuffer4) );
	oBuffer.cut(1,3, oNewBuffer); // Cut from 1 to 3 to new buffer
	checkBuffers ( &oChkBuffer, &oBuffer );
	
	const unsigned char aChkBuffer5[] =  { 0x0B, 0x0C };
	oChkBuffer.replace( aChkBuffer5, sizeof(aChkBuffer5) );
	checkBuffers ( &oChkBuffer, &oNewBuffer );
}

BOOST_AUTO_TEST_CASE ( TestByteBufferMaxCapacity ) {
	const unsigned char aBuffer[] =  { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
	ByteBuffer oBuffer( 1, 3 );

	BOOST_REQUIRE_THROW(
		oBuffer.append(aBuffer, sizeof(aBuffer)),
		SystemException
	);
}

BOOST_AUTO_TEST_CASE ( TestByteBufferMarker ) {
	const unsigned char aBuffer[] =  { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
	ByteBuffer oBuffer( sizeof(aBuffer) );
	oBuffer.append(aBuffer, sizeof(aBuffer));

	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)0 );
	oBuffer.seek ( 3 );
	oBuffer.mark();
	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)3 );
	BOOST_REQUIRE_EQUAL( oBuffer.getLenFromPos(), (unsigned int)(sizeof(aBuffer) - oBuffer.getPos()) );
	oBuffer.seek ( -3 );
	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)0 );
	BOOST_REQUIRE_THROW(
			oBuffer.seek ( 7 ),
			SystemException
	);
	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)0 );
	oBuffer.resetToMarker();

	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)3 );
	oBuffer.clear();
	BOOST_REQUIRE_EQUAL( oBuffer.getLen(), (unsigned int)0 );
	BOOST_REQUIRE_EQUAL( oBuffer.getLenFromPos(), (unsigned int)0 );
	BOOST_REQUIRE_EQUAL( oBuffer.getPos(), (unsigned int)0 );
}
